﻿<?php
    class MooDom {
        function __construct($filename) {
            $this->html = new DOMDocument();
            if(is_file($filename)) {
                $this->html->loadHTMLFile($filename);
            } else {
                $this->html->loadHTML($filename);
            }
        }
        
        public function element($tagname, array $properties = array()) {
            $element = $this->evalHtml('<' . $tagname . '> </' . $tagname . '>');
            return $element;
        }
        
        public function evalHtml($html) {
            $dom = new DOMDocument();
            @$dom->loadHTML($html);
            $domnode = $dom->getElementsByTagName('body');
            $domnode = $domnode->item(0)->childNodes->item(0);
            $domnode = $this->html->importNode($domnode, true);
            $domnode = new MooDomNode($this, $domnode);
            return $domnode;
        }
        
        public function toHtml() {
            return $this->html->saveXML();
        }
        
        public function __toString() {
            return $this->toHtml();
        }
        
        public function run() {
            
        }
        
        public function __get($name) {
            if($name == 'body') {
                $bodies = $this->html->getElementsByTagName('body');
                $node = new MooDomNode($this, $bodies->item(0));
                return $node;
            } else if($name == 'head') {
                $bodies = $this->html->getElementsByTagName('head');
                $node = new MooDomNode($this, $bodies->item(0));
                return $node;
            } else {
                return $this->$name;
            }
        }
    }
    
    class MooDomNode {
        function __construct(&$owner, &$node) {
            $this->owner = $owner;
            $this->node = $node;
            $this->mooid = uniqid();
            $this->setProperty('mooid', $this->mooid);
            $this->events = array();
        }
        
        public function addEvent($type, &$fn) {
            return $this->events[$type][] = $fn;
        }
        
        public function toCssSelector($value) {
            if(strstr($value, '/')) {
                return $value;
            } else {
                $value = preg_replace('/(\.+)/i', '[@${1}]', $value);
            }
        }
        
        public function getElementsByAttribute($attribute, $value) {
            return $this->getElements('//*[@' . $attribute . '="' . $value . '"]');
        }

        public function getElementByAttribute($attribute, $value) {
            $elements = $this->getElementsByAttribute($attribute, $value);
            if(!empty($elements)) {
                return $elements[0];
            } else {
                return false;
            }
        }

        public function getElementById($id) {
            return $this->getElementByAttribute('id', $id);
        }

        public function erase($attribute) {
            $this->node->removeAttribute($attribute);
            return $this;
        }

        public function getElements($q) {
            $query = '';
            $query .= '//' . $this->node->tagName . '[@mooid="' . $this->mooid . '"]';
            $query .= $q;
            $xpath = new DOMXPath($this->owner->html);
            $rows = $xpath->query($query);
            $nodes = array();
            for($i = 0; $i < $rows->length; $i++) {
                $row = $rows->item($i);
                $nodes []= new MooDomNode($this->owner, $row);
            }
            return $nodes;
        }

        public function getElement($q) {
            $elements = $this->getElements($q);
            $element = $elements[0];
            if(!$element) {
                $htmls = $this->owner->html->getElementsByTagName('html');
                $node = new MooDomNode($this->owner, $htmls->item(0));
                $elements = $node->getElements($q);
                $element = $elements[0];
            }
            return $element;
        }

        public function set($name, $value) {
            if($name == 'text') {
                $newtext = new DOMText($value);
                $childs = $this->node->childNodes;
                if($childs) {
                    $nth = 0;
                    $text = $childs->item($nth);
                    while(!$childs->item($nth) instanceof DOMText) {
                        $nth++;
                        $text = $childs->item($nth);
                        if($text === null) {
                            $text = $childs->item($nth - 1);
                            break;
                        }
                    }
                    
                    if($text === null) {
                        $this->node->appendChild($newtext);
                    } else {
                        $this->node->replaceChild($newtext, $text);
                    }
                } else {
                    $this->node->appendChild($newtext);
                }
            } else if($name == 'html') {
                $this->clear();
                $node = $this->owner->evalHtml($value);
                $node->inject($this);
            } else {
                $this->setProperty($name, $value);
            }
            return $this;
        }

        public function get($name) {
            if($name == 'html') {
                $html = '';
                if($this->node->childNodes) {
                    foreach($this->node->childNodes as $node) {
                        $html .= $node->ownerDocument->saveXML($node);
                    }
                } else {
                }
                return $html;
            } else if($name == 'text') {
                return $this->node->textContent;
            } else if($name == 'tag') {
                return $this->node->tagName;
            } else {
                return $this->getProperty($name);
            }
        }
        
        public function getParent() {
            if($this->node->parentNode) {        
                $parent = new MooDomNode($this->owner, $this->node->parentNode);
                $this->parent = $parent;
                return $parent;
            }
            return false;
        }
        
        public function copy($contents = true, $keepid = false) {
            $dom = new MooDom($this->toHtml());
            $domnode = $dom->html->getElementsByTagName('body')->item(0)->firstChild;
            $domnode = new MooDomNode($dom, $domnode);
            if(!$keepid) {
                $domnode->set('id', '');
            }
            if(!$contents) {
                $domnode->clear();
            }
            $domnode->mooid = uniqid();
            $domnode->setProperty('mooid', $domnode->mooid);
            return new MooDomNode($domnode->owner, $domnode->node);
            
        }
        
        public function setProperty($prop, $value) {
            $this->node->setAttribute($prop, $value);
            return $this;
        }
        
        public function getProperty($prop) {
            return $this->node->getAttribute($prop);
        }
        
        public function getDocument() {
            return $this->node->ownerDocument;
        }
        
        public function toHtml() {
            $html = $this->getDocument()->saveXML($this->node);
            return $html;
        }
        
        public function inject($node, $location = 'bottom') {
            if($this->node->ownerDocument !== $node->node->ownerDocument) {
                $this->node = $node->node->ownerDocument->importNode($this->node, true);
            }
            $this->parent = $node;
            if($location == 'bottom') {
                $this->node = $node->node->appendChild($this->node);
            } else if($location == 'top') {
                $first = $node->node->firstChild;
                if($first) {
                    $this->node = $node->node->insertBefore($this->node, $first);                    
                } else {
                    $this->node = $this->inject($this->node, 'bottom'); 
                }
            } else if($location == 'after') {
                $sibling = $node->node->nextSibling;
                if($sibling) {
                    $this->node = $node->node->parentNode->insertBefore($this->node, $sibling);
                } else {
                    $this->node = $this->inject($node->node->parentNode, 'bottom');
                }
            }
            return $this;
        }
        
        public function getFirst() {
            $first = $this->node->childNodes->item(0);
            if($first) {
                return new MooDomNode($this->owner, $first);
            } else {
                return false;            
            }
        }
        
        public function dispose() {
            return $this->node->parentNode->removeChild($this->node);
        }
        
        public function clear() {
            if($this->node->hasChildNodes()) {
                while($this->node->childNodes->length) {
                    $this->node->removeChild($this->node->firstChild);
                }
            }
            return $this;
        }
    }
    
    function MooDom($filename) {
        @$document = new MooDom($filename);
        return $document;
    }
?>